import { RouteRecordRaw } from 'vue-router';
import { SysMenuApi } from '/@/api/sysApi/SysMenuApi.ts';
import { getChildrenListMethodDefault, TreeUtil } from '/@/utils/TreeUtil.ts';
import _ from 'lodash';
import { generateDefaultRouterList } from '/@/router/Router.ts';

/**
 * 菜单类
 */
export class Menu {
  // 路由树，从后端获取菜单树后将其按照路由对象的结构转换为此对象
  // 其为层次结构，而非添加动态路由后的router.getRoutes()的展平结构。
  // 层次结构可以方便的用于菜单的展示
  private readonly _routerTree: RouteRecordRaw[]

  // 默认路由与从后端获取的菜单的结合体，包含了当前用户具有所有的路由信息
  // 其与vue的路由对象中的路由数据是完全分离的，不参与路由，仅用于展示菜单数据
  // 可理解为_routerTree的扩展
  private readonly _finalRouterTree: RouteRecordRaw[] = generateDefaultRouterList()

  // 菜单树，从后端获取的原始数据
  private readonly _menuTree: SysMenuApi.MenuType[]

  constructor(menuTree: SysMenuApi.MenuType[]) {
    this._menuTree = menuTree
    // 将菜单树转换为路由树
    this._routerTree = Menu.toRouter(this._menuTree)
    // 将routerTree合并到_routerTree中
    this.mergingRouterTree(_.cloneDeep(this._routerTree), this._finalRouterTree)
  }

  /**
   * 将菜单树转换为路由树，转换后的对象是一个全新对象。这是一个递归方法
   * @param menuTree 菜单树
   */
  static toRouter = (menuTree: SysMenuApi.MenuType[]): RouteRecordRaw[] => {
    const routeRecordRawList: RouteRecordRaw[] = []
    menuTree.map((menu: SysMenuApi.MenuType) => {
      const routeRecordRaw: RouteRecordRaw = {
        path: menu.path!,
        name: menu.name,
        children: [],
        meta: {
          title: menu.title,
          icon: menu.icon,
          type: menu.type,
          color: menu.color,
          layoutName: menu.layoutName,
        },
      }
      // 如果有组件路径，则加载组件
      if (menu.componentPath) {
        routeRecordRaw.component = () => import(/* @vite-ignore */ `/@/views/${menu.componentPath}.vue`)
      }
      // 如果有子菜单或目录，则继续递归
      if (menu.children && menu.children.length != 0) {
        routeRecordRaw.children = Menu.toRouter(menu.children)
      }
      routeRecordRawList.push(routeRecordRaw)
    })
    return routeRecordRawList
  }

  /**
   * 将target合并到source<br />
   * 通过target的meta.layoutName，在source的name中寻找，如果找到则将这一条target追加到对应source的children中<br />
   * 仅处理target的顶级节点即可达到效果
   * @param targetRouterTree 要合并的路由树
   * @param sourceRouterTree 被合并的路由树（合并到这个对象里面）
   */
  mergingRouterTree = (targetRouterTree: RouteRecordRaw[], sourceRouterTree: RouteRecordRaw[]) => {
    // 遍历target的顶级节点
    targetRouterTree.map((targetRouteItem: RouteRecordRaw) => {
      // 深度遍历source，查找符合条件的节点
      const sourceRouteFound = TreeUtil.findTree(
        sourceRouterTree,
        getChildrenListMethodDefault,
        (sourceRouteItem: RouteRecordRaw): boolean =>
          // 这个是查找条件，即source的meta.layoutName与target的name相等时返回true
          (sourceRouteItem.name &&
            targetRouteItem.meta?.layoutName &&
            sourceRouteItem.name === targetRouteItem.meta.layoutName) as boolean
      )
      // 如果找到了，则将target追加到其children中。若未找到，则直接追加到source的顶级
      if (sourceRouteFound) {
        if (!sourceRouteFound.children) {
          sourceRouteFound.children = []
        }
        sourceRouteFound.children.push(targetRouteItem)
      } else {
        sourceRouterTree.push(targetRouteItem)
      }
    })
  }

  /**
   * 根据路由名称查找路由树
   */
  findFinalRouteTree(name: string): RouteRecordRaw | undefined {
    return TreeUtil.findTree(
      this._finalRouterTree,
      getChildrenListMethodDefault,
      (routerItem: RouteRecordRaw) => routerItem.name === name
    )
  }

  get getRouteTree(): RouteRecordRaw[] {
    return this._routerTree
  }

  get getFinalRouteTree(): RouteRecordRaw[] {
    return this._finalRouterTree
  }

  get getMenuTree(): SysMenuApi.MenuType[] {
    return this._menuTree
  }
}
